#include "metanet_handle.h"

#include <stdlib.h>
#include <assert.h>
#include <string.h>

#include "metanet_malloc.h"
#include "metanet_server.h"

#define DEFAULT_SLOT_SIZE 4
#define DEFAULT_NAME_COUNT 4
#define MAX_SLOT_SIZE 0x40000000

struct metanet_handle_name {
    char *name;
    uint32_t handle;
};

struct metanet_handle_storage {
    uint32_t harbor;
    uint32_t handle_index;
    int slot_size;
    struct metanet_context **slot;

    int name_cap;
    int name_count;
    struct metanet_handle_name *name;
};

static struct metanet_handle_storage *H = NULL;

uint32_t 
metanet_handle_register(struct metanet_context *ctx) {
    struct metanet_handle_storage *s = H;
    for(;;) {
        int i;
        for (i = 0; i < s->slot_size; ++i) {
            uint32_t handle = (i+s->handle_index) & HANDLE_MASK;
            int hash = handle & (s->slot_size-1);
            if (s->slot[hash] == NULL) {
                s->slot[hash] = ctx;
                s->handle_index =  handle + 1;

                handle |= s->harbor;
                return handle;
            }
        }
        assert((s->slot_size*2 - 1) <= HANDLE_MASK);
        struct metanet_context **new_slot = (struct metanet_context **)metanet_malloc(s->slot_size * 2 * sizeof(struct metanet_context*));
        memset(new_slot, 0, s->slot_size * 2 * sizeof(struct metanet_context*));
        for (i = 0; i < s->slot_size; ++i) {
            int hash = metanet_context_handle(s->slot[i]) & (s->slot_size*2 - 1);
            assert(new_slot[hash] == NULL);
            new_slot[hash] = s->slot[i];
        }
        metanet_free(s->slot);
        s->slot = new_slot;
        s->slot_size *= 2;
    }
}

struct metanet_context *
metanet_handle_grab(uint32_t handle) {
    struct metanet_handle_storage *s = H;
    struct metanet_context *retsult = NULL;

    uint32_t hash = handle & (s->slot_size - 1);
    struct metanet_context *ctx = s->slot[hash];
    if (ctx && metanet_context_handle(ctx) == handle) {
        retsult = ctx;
        metanet_context_grab(retsult);
    }
    return retsult;
}

int 
metanet_handle_retire(uint32_t handle) {
    int ret = 0;
    struct metanet_handle_storage *s = H;

    uint32_t hash = handle & (s->slot_size - 1);
    struct metanet_context *ctx = s->slot[hash];
    if (ctx != NULL && metanet_context_handle(ctx) == handle) {
        s->slot[hash] = NULL;
        ret = 1;
        int i;
        int j = 0;
        int n = s->name_count;
        for (i = 0; i < n; ++i) {
            if (s->name[i].handle == handle) {
                metanet_free(s->name[i].name);
                continue;
            } else if (i != j) {
                s->name[j] = s->name[i];
            }
            ++j;
        }
        s->name_count = j;
    } else {
        ctx = NULL;
    }

    if (ctx) {
        metanet_context_release(ctx);
    }

    return ret;
}

void 
metanet_handle_retireall() {
    struct metanet_handle_storage *s = H;
    for (;;) {
        int n = 0;
        int i;
        for (i = 0; i < s->slot_size; ++i) {
            struct metanet_context *ctx = s->slot[i];
            uint32_t handle = 0;
            if (ctx) {
                handle = metanet_context_handle(ctx);
            }
            if (handle != 0) {
                if (metanet_handle_retire(handle)) {
                    ++n;
                }
            }
        }
        if (n == 0) {
            return;
        }
    }
}

uint32_t 
metanet_handle_findname(const char *name) {
    struct metanet_handle_storage *s = H;
    
    uint32_t handle = 0;

    int begin = 0;
    int end = s->name_count - 1;
    while (begin <= end) {
        int mid = (begin + end) /2;
        struct metanet_handle_name *n = &s->name[mid];
        int c = strcmp(n->name, name);
        if (c == 0) {
            handle = n->handle;
            break;
        }
        if (c < 0) {
            begin = mid + 1;
        } else {
            end = mid - 1;
        }
    }

    return handle;
}

static void
_insert_name_before(struct metanet_handle_storage *s, char *name, uint32_t handle, int before) {
    if (s->name_count >= s->name_cap) {
        s->name_cap *= 2;
        assert(s->name_cap <= MAX_SLOT_SIZE);
        struct metanet_handle_name *n = (struct metanet_handle_name *)metanet_malloc(s->name_cap * sizeof(struct metanet_handle_name));
        int i;
        for (i = 0; i < before; ++i) {
            n[i] = s->name[i];
        }
        for (i = before; i < s->name_count; ++i) {
            n[i+1] = s->name[i];
        }
        metanet_free(s->name);
        s->name = n;
    } else {
        int i;
        for (i = s->name_count; i > before; --i) {
            s->name[i] = s->name[i-1];
        }
    }

    s->name[before].name = name;
    s->name[before].handle = handle;
    ++s->name_count;
}

static const char *
_insert_name(struct metanet_handle_storage *s, const char *name, uint32_t handle) {
    int begin = 0;
    int end = s->name_count - 1;
    while (begin <= end) {
        int mid = (begin + end) / 2;
        struct metanet_handle_name *n = &s->name[mid];
        int c = strcmp(n->name, name);
        if (c == 0) {
            return NULL; // find
        }
        if (c < 0) { // n->name < name sheng xu
            begin = mid + 1;
        } else {
            end = mid - 1;
        }
    }
    char *result = metanet_strdup(name);
    _insert_name_before(s, result, handle, begin);
    return result;
}

const char *
metanet_handle_namehandle(uint32_t handle, const char *name) {
    const char *ret = _insert_name(H, name, handle);
    return ret;
}

void 
metanet_handle_init(int harbor) {
    assert(H==NULL);
    struct metanet_handle_storage *s = (struct metanet_handle_storage *)metanet_malloc(sizeof(*H));
    s->slot_size = DEFAULT_SLOT_SIZE;
    s->slot = (struct metanet_context **)metanet_malloc(s->slot_size * sizeof(struct metanet_context*));
    memset(s->slot, 0, s->slot_size * sizeof(struct metanet_context*));

    s->harbor = (uint32_t)(harbor & 0xFF) << HANDLE_REMOTE_SHIFT;
    s->handle_index = 1;
    s->name_cap = DEFAULT_NAME_COUNT;
    s->name_count = 0;
    s->name = (struct metanet_handle_name*)metanet_malloc(s->name_cap * sizeof(struct metanet_handle_name));

    H = s;
}

void metanet_handle_exit() {
    if (H != NULL) {
        int i;
        for (i = 0; i < H->slot_size; ++i) {
            metanet_free(H->slot[i]);
        }
        metanet_free(H->slot);

        for (i = 0; i < H->name_count; ++i) {
            metanet_free(H->name[i].name);
        }
        metanet_free(H->name);
    }
}